package org.NooLab.glue.components;



import java.util.*;

import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;

import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.components.pkg.TaskPackage;
import org.NooLab.glue.filter.BoardMessageInterpreter;
import org.NooLab.glue.instances.*;
import org.NooLab.glue.interfaces.ReceptorDigestIntf;
import org.NooLab.glue.msgframe.*;
import org.NooLab.glue.net.*;
import org.NooLab.glue.storage.MsgBoardDataStorage;
import org.NooLab.glue.transaction.Transaction;
import org.NooLab.glue.transaction.Transactions;



public class MessageBoardConnector implements Observer{

	MessageBoardFactory mbf ;
	MessageBoard mb ;
	MessageBoardFactoryProperties factoryProperties;
	
	MsgBoardDataStorage msgBoardStorage;
	
	MessageBoardShareIntf msgBoardShares;
	BoardAddresses boardAddresses;
	
	ComBoxIntf combox;
	DataReceiverIntf receiver ;
	DataSenderIntf sender;
	
	ParticipantSourceIntf msgBoardAsParticipant;
	MessageBoardSrcInstance msgBoardSrcInstance ;
	
	BoardMessagesDigester msgDigester;
	DataConnector dataConnector;
	BXMessage xmsg ;
	XRegistration xreg;
	
	BoardAddress selectedRemoteBoardAddress ;
	Map<String, Object> connectedBoardSources = new HashMap<String, Object>();
	
	Vector<BoardInterpreterMessage> iboardMessages = new Vector<BoardInterpreterMessage>(); 
	BoardInterpreterMessageHandler iboardMsgHandler ;
	BoardMessageSingleReply iboardMsgSingleReplies ;
	
	Vector<Long> currentlyRelayedTransactions = new Vector<Long>(); 
	PrintLog out;
	
	
	 
	/**
	 * 
	 * actually, what we need is a kind of internal participant, in order NOT to interfere with 
	 * the normal activities of the MessageBoard.
	 * This iPart will have its own ComBoxFactory, and completely private settings....
	 * Nevertheless, we could set callbacks to the MessageBoard (through our local Digester here), which 
	 * in case of standard clients does not exist 
	 * 
	 * 
	 * here also: unique application first have to send an unregister !!!
	 *            send unregister on closing the Board
	 *            interboard messages ALWAYS have to contain the name
	 *            the local board maintains a map of names to addresses
	 *            -> used in unregister, -> used in routing messages and creating responses
	 *               not just unregister of the last one
	 *               
	 *            change where necessary : =="source"  -> MessageBoardFactoryProperties.isSourceType(role) 
	 *             
	 *            no uplink connection to type MessageBoardFactoryProperties.variant_BSOURCE 
	 *             
	 *            http-rest source can maintain relations to many target = which in this case 
	 *            are remote MessageBoards ;
	 *            
	 *            adapt to different callbacks, check it !! -> all should be routed to the MessageBoard
	 *            
	 *              
	 * @param mbf
	 * @param mb
	 * @param outprn
	 */
	public MessageBoardConnector( MessageBoardFactory mbf, MessageBoard mb , PrintLog outprn){
		
		this.mbf = mbf;
		this.mb = mb;
		
		factoryProperties = mbf.getFactoryProperties() ;
		
		msgBoardShares = mb.getMsgBoardShares();
		boardAddresses = msgBoardShares.getBoardAddresses() ;
		
		msgBoardStorage = mbf.getMessageBoardStorage();
		
		combox = mbf.comBoxFactory.getComBox() ;
		
		out = outprn;
		out = new PrintLog(2, true);
		
		xreg = mb.getMxReg() ; 
		xreg.init(mbf, (ReceptorDigestIntf)msgDigester) ;
		
		msgDigester = new BoardMessagesDigester( mbf ,mb, this) ;
		// dataConnector = new DataConnector( mbf, xreg , (ReceptorDigestIntf)msgDigester );
		
		
		xmsg = new BXMessage(out) ;
		
		iboardMsgHandler = new BoardInterpreterMessageHandler(this,mb); 
		iboardMsgSingleReplies = new BoardMessageSingleReply( mbf, this, out) ;
		
		
		System.gc() ;
	}
	
	
	@Override
	public void update(Observable arg0, Object xmlstrobj) {
		String str, hstr="";
		
		str = (String)xmlstrobj ;
		                                        if (str.contains("<connect>")){
		                                        	hstr ="(msg = <connect>)";
		                                        }
												out.print(2, "\n===============================\n"+
															 "update by Observer in MessageBoardConnector{} "+hstr+"... \r\n");
												out.print(2, str);
		 
		Message msg = new Message ();
		msg.rawXmlMsg = (String)xmlstrobj;
		
		// here we take ONLY connect polling messages
		if (str.contains("<connect>")){
			BoardMessageInterpreter mi = mb.getBoardMessageInterpreter();
			mi.interpretingSingleBoardMessage(msg, this) ;
		}
	}

	public void closeEmbeddedSource(){
		String host;
		
		if ((boardAddresses==null) || (boardAddresses.size()==0)){
			return;
		}
		
		for (int i=0;i<boardAddresses.getActive().size();i++){
			host = boardAddresses.getActive().get(i).getAddress().getHostname() ;
			
			if ( connectedBoardSources.containsKey(host) ){
				msgBoardSrcInstance = (MessageBoardSrcInstance) connectedBoardSources.get(host) ; 
				
				if (msgBoardSrcInstance!=null){
					msgBoardSrcInstance.disconnect() ;
				}
			}
		} // i->
			
	}
	
	
	/**
	 * 
	 * the low level check of the connection reveled that it is not available any more (machine down or MessageBoard down)
	 * so we remove the instance
	 *  
	 * @param boardAddress
	 */
	synchronized public void removeBoardAddress( BoardAddress boardAddress ){
		String host;
		int ix;
		
		host = boardAddress.getAddress().getHostname() ;
		
		ix = mbf.boardAddresses.indexOf( host, 0) ;
		if (ix<0){
			return;
		}
		
		if (connectedBoardSources.containsKey(host)){
		
			msgBoardSrcInstance = (MessageBoardSrcInstance) connectedBoardSources.get(host);
			connectedBoardSources.remove(host) ;
			msgBoardSrcInstance = null;
		}
		
	}
	
	public void interpreteInterBoardMsg( BoardInterpreterMessage ibordMsg){
		
		String[] rawIndicators;
		String rawXmlMsg;
		
		
		try{
												out.print(2, "MessageBoardConnector, internal message handler, interpreteInterBoardMsg(), action = "+ibordMsg.action+" ...");
			
			// dependent on ....  create a new message
			// ibordMsg.action = "performRelay" ;
			
			rawIndicators = new String[]{"<transaction>","secret=", "<instance", "<confirmation", "type=\"relay"} ;
			if (iboardMsgSingleReplies.performTaskRelay( ibordMsg.rawXmlMsg, rawIndicators, ibordMsg.action)){ return; }; 
				
			 
			
			
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}
	
	
	
	/**
	 * 
	 * 
	 * 
	 * @param boardAddress
	 */
	public void sendIBoardPing( BoardAddress boardAddress){
		BasicMessageParams basics;
		String xmlstr;
		String host;
		int port ,ix, mutualRelay=0;
		
		
		
		if (boardAddresses.getActive().size() > 50){
			return ;
		}
		
		basics = new BasicMessageParams();
		
		host = boardAddress.getAddress().getHostname() ;
		port = boardAddress.getAddress().getPort() ;
		
		basics.roleOfSender = MessageBoardFactoryProperties.variant_BSOURCE;
		
		basics.roleOfSender = "IBOARD" ; // note, that interboard communication needs iboard,
										 // clients receive message of role "BOARDS" as default due to relaying
		
		// not yet.... this here refers to the board, yet, we need the embedded source
		// basics.instanceName = xreg.getNameOfApplicant() ; 	
		
		 
        /*
          	<messageboard name="spela" role="IBOARD">
   				<register>
   				<connect action="ping">
      				<instance name="mb1-board-a7aa8027-0c26-4c3e-9e9d-b238cddae39f"/>
   				</connect>
   				</register>
			</messageboard>
         */
        
        // this address now checked for availability , already registered ?
		
		ix = mbf.boardAddresses.indexOf( host, 0) ;
		if (ix<0){
			return;
		}
		
		selectedRemoteBoardAddress = mbf.boardAddresses.get(ix);
		
		if (selectedRemoteBoardAddress.isRegistering()){
			return;
		}
		// do not try to register again if we already do...
		// boolean isConfirmed=false;
		// boolean isRegistering=false;
		if (mbf.getFactoryProperties().getRelayingIsMutual()){
    		mutualRelay =1;
    	}
		 
											out.print(4, "sending a ping to remote MessageBoard (addr:"+host+":"+port+"), object : "+this.toString());
        if ( connectedBoardSources.containsKey(host)==false ){
        	
        	// this will automatically connect
        	selectedRemoteBoardAddress.setRegistering(true) ;
        	
        	
        	
        	msgBoardSrcInstance = new MessageBoardSrcInstance(factoryProperties, mbf, mb, this, selectedRemoteBoardAddress , out);
        	
        	int z=0;
        	while ((msgBoardSrcInstance.connected==false) && (z<4000)){ // max 40 seconds...
        		out.delay(10);
        		z++;
        		if (msgBoardSrcInstance.participant != null){
        			break;
        		}
        	}
        	
        	
        	
        	if ((msgBoardSrcInstance!=null) ){
        	
        		msgBoardAsParticipant =  msgBoardSrcInstance.participant ;
        		
        		selectedRemoteBoardAddress.setRegistering(false) ;
        		selectedRemoteBoardAddress.setConfirmed(true) ;
        		
        		connectedBoardSources.put(host, msgBoardSrcInstance);
        		
        		 
        		basics.instanceName = msgBoardSrcInstance.name;
        		
        		// this also introduces a text segment "$checkaddress$", which we replace later...
        		xmlstr = xmsg.createIboardPing(basics ,mutualRelay);
        		xmlstr = addRestletRoutingPrefix( xmlstr, "r","");
        		// uses the virtual port for interboard communication 7007 -> route = /vp7007 
        		if (msgBoardAsParticipant!=null){
        			msgBoardAsParticipant.send( xmlstr, 0,this);
        		}
        		
        	}else{
        		if (msgBoardAsParticipant!=null){
        			msgBoardSrcInstance.disconnect() ;
        		}
        		
        		msgBoardSrcInstance = null;
            	System.gc();
        	}
        	
        }else{
        	msgBoardSrcInstance = (MessageBoardSrcInstance) connectedBoardSources.get(host);
        	if (msgBoardSrcInstance != null){
        		if ((msgBoardAsParticipant!=null) || (msgBoardSrcInstance.connected )){
        			
        			basics.instanceName = msgBoardSrcInstance.name;
            		xmlstr = xmsg.createIboardPing(basics , mutualRelay);
            		xmlstr = addRestletRoutingPrefix( xmlstr, "r","");
            		
            		msgBoardAsParticipant.send( xmlstr, 0,this); // 0 -> using directSend()
        		}
        	}
        } // 
        
        
        
	}
	 
	
	/**
	 * 
	 * if the whole action fails, we have to reset the state of the transaction !!!
	 * 
	 * 
	 * @param transactionID
	 */
	
	public int startTransactionRelay( long transactionID ){
		
		// we need the whole XML, the doc type,
		int result = -1;
		int bn;
		boolean relayIsAllowed;
		String xmlout="", xmlstr="", hostaddress="", guidStr,ctype="", relayName, filename; 
		long instanceID, tid;
		BasicMessageParams basics;
		Transaction transaction=null;
		Transactions transactions;
		MessageBoardSrcInstance msgBoardSrc;
		
		try{
			
			// are there still any remote boards?
			
			bn = mbf.boardAddresses.countByState( BoardAddress.__BA_KNOWNMAP, 1);
			
			if (bn<=0){
				return -3;
			}
			
			transactions = mbf.getTransactions() ;
			
			if (transactions!=null){
				transaction = transactions.getTransactionByID(transactionID) ;
			}
			
			if (transaction!=null){
				
				ctype = transaction.getContentType() ;
			
				tid = transaction.getTransactionID() ;
				instanceID = transaction.getInstanceID() ;
				guidStr = GUID.randomvalue() ;
				relayName = msgBoardSrcInstance.name;
				
				 
				filename = msgBoardStorage.createTransactionDataObjTmpFilename( transaction.getTransactionID(), 0 );
				filename = filename.replace("/~tdata_", "/~reqray_") ;

				// the XML as relayed to the RECEPTOR in direct transport: => this will be delivered
				Object obj = msgBoardStorage.readTransactionDataObj( filename) ;
				if (obj instanceof Integer){
					
					out.printErr(2, "Relaying transaction task failed, bufferfile not found...");
					return -3;
				}
				xmlstr = (String)obj ;
				
				 
				int relayflag = (int) xmsg.getSpecifiedValue(xmlstr, "//transaction/taskrelay", "value");
				int allowedHops = (int) xmsg.getSpecifiedValue(xmlstr, "//transaction/taskrelay", "hopcount") ;
				if (relayflag==0){
					// if -1 not avail -> allowed
					return 0;
				}
				
				// adding the resource secret
			    // xmlstr = xmsg.insertRawToXml( guidStr, "resource","value", xmlstr, "</messageboard>",1);
				/*
			     	<linkrelation value="2"/>
      				<relationitemid value="-1"/>
      				<relationgroupid value="-1"/>
			    */
			 

			    BoardAddress boardAddress;
			    boardAddress = mbf.boardAddresses.getByStateFirst( BoardAddress.__BA_KNOWNMAP, 1);
			    if (boardAddress==null){
			    	return -5;
			    }

						 basics = new BasicMessageParams();
			    		 basics.instanceID = instanceID;
			    		 basics.transactionID = tid;
			    		 basics.instanceName = relayName;
			    		 basics.privateTransactionIdentifier = guidStr ;
			    		 basics.roleOfSender = "IBOARD";
			    		 basics.hostaddress = boardAddress.getAddress().getHostname();
			    		 
			    		 
			    // TODO: do not forget about the <context> section !!	
						 
			    xmlout = xmsg.createRelayDoctypeRequest( basics , ctype, "");

			   
			    hostaddress = boardAddress.getAddress().getHostname() + ":" + boardAddress.getAddress().getPort() ;
			    
			    xmlout = addRestletRoutingPrefix( xmlout, "d" ,""); //  hostaddress);
			    
			    msgBoardSrc = (MessageBoardSrcInstance) connectedBoardSources.get( boardAddress.getAddress().getHostname() );
			    if (msgBoardSrc!=null){
			    	// doing so, we automatically have the correct ipaddress etc !!!
			    	// TODO we have to limit the number of embedded clients (each one is linked to an address!)
			    	
			    	// we put the ID of the transaction into the list of transactions
			    	//  - protect against other processes
			    	//  - correcting state in case of failure
			    	currentlyRelayedTransactions.add(tid) ;
			    	transaction.setTransactState( Transaction.TS_TASK_RELAYING ) ;
			    	msgBoardSrc.xsend( xmlout, mb ); // mb instead of this 
			    	// will be handled by .....
			    	
			    }
			    
			    result=0;
			} // transaction!=null ?
			
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return result;
	}
	
	
	protected String addRestletRoutingPrefix( String xmlstr, String route, String hostaddress){
		String xmlRestPrefix ="",str,xmlout;
		
		xmlout = xmlstr;
		if (mbf.getFactoryProperties().getProtocolId() <= MessageBoardFactoryProperties.protocolFTP){
			
			if (route.toLowerCase().startsWith("d")){ route= "/data"; }
			if (route.toLowerCase().startsWith("r")){ route= "/relay"; }
			
			xmlRestPrefix = xmsg.createRestletRoutingPrefix("", route, "post");
			
			if (hostaddress.length()>0){
				xmlRestPrefix = xmlRestPrefix.replace("::", "");
				str = xmsg.changeAddSegment(xmlRestPrefix, "//meta", "host", "addr", hostaddress) ;
				
				if (str.length()>0){
					xmlRestPrefix = str;
				}else{
					str = "<host addr=\""+hostaddress+"\" />";
					xmlRestPrefix = xmlRestPrefix.replace("</meta>", str+"\n</meta>");
				}
				xmlRestPrefix = xmlRestPrefix.trim() +"::" ;
			}
			
			xmlout = xmlRestPrefix + xmlout  ;
		}
		
		return xmlout;
	}


	public Vector<BoardInterpreterMessage> getIboardMessages() {
		return iboardMessages;
	}

 
}


class BoardMessageSingleReply{
	
	MessageBoardFactory mbf;
	MessageBoardConnector mbc;
	
	BXMessage xmsg ;
	PrintLog out;
	
	
	public BoardMessageSingleReply( MessageBoardFactory mbf, MessageBoardConnector mbc, PrintLog outprn){
		
		out = outprn;
		xmsg = new BXMessage(out) ;
	
		this.mbf = mbf;
		this.mbc = mbc;
	}
	
	public boolean performTaskRelay(String rawXmlMsg, String[] rawIndicators, String actionDescriptor){
		/*
			<messageboard name="spela" role="IBOARD">
   				<transaction>
       				<transaction id="7"/>
       				<instance name="ea0526c3-9748-4be3-aba1-667d765dd6aa"/>
       				<confirmation availability="1" secret="fc4b42aa-828f-44fc-9f9a-c75299ba60c6" transactionid="694" type="relay"/>
    			</transaction>
		 */
		boolean result=false;
		String str, xmlOutStr="",rmbName="", transactionSecret, bufferfilename, taskXmlStr;
		int availReceptorCount, ix;
		long tid,iid,remoteProcessTid;
		Transaction transaction;
		
		
		result = (xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators) ) || (actionDescriptor.contains("performRelay"));
		
		if ( result ){
												out.print(2, "confirmation received from remote MessageBoard, now preparing task relay ...." ) ;
			// the xml has been created by "createRelayRequestResponse()"
			availReceptorCount = (int) xmsg.getSpecifiedValue(rawXmlMsg, "//transaction/confirmation", "availability") ;
			
			rmbName = xmsg.getSpecifiedInfo(rawXmlMsg, "instance", "name") ;

			remoteProcessTid = (int) xmsg.getSpecifiedValue(rawXmlMsg, "//transaction/transactionid", "value") ;
			
			// <confirmation  transactionid="1111"
			  
			tid = (int) xmsg.getSpecifiedValue(rawXmlMsg, "//transaction/confirmation", "transactionid") ;
			ix = mbc.currentlyRelayedTransactions.indexOf(tid);
			
			
			ix = mbf.getTransactions().getIndexByTransactionID(tid, 1) ;
			transaction = mbf.getTransactions().getItem(ix) ;
 
			if (availReceptorCount<=0){
												out.print(2, "remote board says 'no available receptors' ... " ) ;
				// reset the state of the transaction
				transaction.setTransactState( Transaction.TS_TASK_ACCEPTED ) ;
				
				return result;
			}
			
			
			transactionSecret = xmsg.getSpecifiedInfo(rawXmlMsg, "confirmation", "secret");
			
			// get the SOURCE-based transaction , as xml
			iid = transaction.getInstanceID();
			
			// bufferfilename = msgBoardStorage.createTransactionBufferfileName( instanceID,  transactionID, role, "BOARD") ;
			
			bufferfilename =  mbf.getMessageBoardStorage().createTransactionDataObjTmpFilename( transaction.getTransactionID(), 0 );
			bufferfilename = bufferfilename.replace("/~tdata_", "/~reqray_") ;
			taskXmlStr = (String)mbf.getMessageBoardStorage().readTransactionDataObj( bufferfilename) ;
			
			if ((taskXmlStr.contains("<data")) || (taskXmlStr.contains("<values>"))){
				
			} // is 
			// add some important nodes about relaying,
			/*
			    <relay>
			       	<hopcount value="2"/>
			       	<station name="" />
			       	<transaction id="" secret="y" />
			 	</relay>
			 */
			xmlOutStr = xmsg.changeAddSegment(taskXmlStr,"//transaction","relay/station", "name", rmbName);
			// 1. this causes a problem: many relay sections are introduced !!! 
			//    all the nodes before the last need to be tested for existence if the last one does not exist !!!
			
			// 2. +1 crashes if hopcount is not present
			xmlOutStr = xmsg.changeAddSegment(xmlOutStr,"//transaction","relay/transaction", "id",""+remoteProcessTid );
			xmlOutStr = xmsg.changeAddSegment(xmlOutStr,"//transaction","relay/transaction", "secret", transactionSecret);
			
			// adding / changing the info about relayHopCount... elegantly increasing by 1
		    // if it does not exist so far, it will be added
			xmlOutStr = xmsg.changeAddSegment(xmlOutStr,"//transaction","relay/hopcount", "value","+1");
		    
												out.print(2, "relaying transaction (tid:"+transaction.getTransactionID()+") to remote MessageBoard");
			// send
			xmlOutStr = mbc.addRestletRoutingPrefix( xmlOutStr, "d","");
			mbc.msgBoardAsParticipant.send( xmlOutStr, 0,mbc);
			// the response enters through MessageBoard itself!!
    		// only on confirmation by the receiving relay-target board, 
			// we will change the status of the transaction, and appears as a piece from role=SOURCE !!!
			/*
			  
                <taskrelay hopcount="-1" value="1"> -> allowed, no limit for hopcount 			 
			 	<relay>
					<station name="x"/>
					<transaction id="-1" secret="c0be45fb-9003-4ada-a879-779bd65234a5"/>
					<hopcount value="1"/>
				</relay>
			 */
			
		} // correct signature
		
		return result;
	}
	
	
}

/**
 * 
 * similar to the MessageBoard
 * 
 * @author kwa
 *
 */
class BoardInterpreterMessageHandler implements Runnable{
	
	MessageBoardConnector mbc;
	MessageBoard msgboard;
	
	boolean isRunning ;
	boolean isWorking;
	
	private Thread thrd;
	
	
	public BoardInterpreterMessageHandler( MessageBoardConnector mbc, MessageBoard mb){
		this.mbc = mbc;
		msgboard = mb ;
		
		thrd = new Thread(this, "MessageBoardConnector-iboardmsg");
		thrd.start() ;
	}

	// works on iboardMessages ....
	public void run() {
		int d=0;
		isRunning = true;
		 
		
		try{
 		
			while ( (isRunning)  ) {
				
				if (isWorking == false) {
					if (mbc.iboardMessages.size() > 0) {
						d=10;
						isWorking = true;
						
						mbc.interpreteInterBoardMsg( mbc.iboardMessages.get(0) );
						
						if (mbc.iboardMessages.size() > 0){
							mbc.iboardMessages.remove(0);
						}
						
						isWorking = false;
					}
				}
				{
					mbc.out.delay(10) ;
				}
				
			} // while ...
			
		}catch(Exception e){
			
		}finally{
			isRunning = false;
			isWorking = false;
		}
		
	}
	
	
	
	
}


class BoardMessagesDigester implements ReceptorDigestIntf{
	
	MessageBoardFactory mbf ;
	 
	MessageBoardConnector parent ;
	// TaskPackage resultPackage;
	
	public BoardMessagesDigester( MessageBoardFactory mbf, MessageBoard mb, MessageBoardConnector pparent ){
		this.mbf = mbf ;
		parent = pparent ;
		 
	}

	@Override
	public void initializeMessageBoardConnection(int physReceptor, XRegistration xreg) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public XRegistration getSourceRegistration() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void performTask(TaskPackage taskPackage) {
		 
		parent.out.print(2, "MessageBoardConnector, mrd -> taskpackage message received ..." );
	}

	@Override
	public void performSystemTask(String taskParameters) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void handlingNonXmlContent(String str) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void performTaskRelay(String rawXmlStr) {
		// TODO Auto-generated method stub
		parent.out.print(2, "MessageBoardConnector, mrd -> performTaskRelay() ..." );
	}

	@Override
	public void setSendingConfirmed(boolean flag) {
		// TODO Auto-generated method stub
		
	}
	
	
}	


class MessageBoardSrcInstance implements Runnable, ResultReceiverIntf{ 

	// 
	ParticipantFactory factory ;
	MessageBoardFactoryProperties factoryProperties, importedFactoryProps ;
	ParticipantSourceIntf participant ;
	 
	MessageBoardFactory mbf;
	MessageBoard mb ;
	MessageBoardConnector parent;
	
	boolean connected = false;
	
	String name ;
	String remotehostname; 
	int remoteport;
	
	// helper objects
	Thread iThrd; 
	PrintLog out  ;
	
	
	public MessageBoardSrcInstance( MessageBoardFactoryProperties factprops ,  
									MessageBoardFactory mbf, 
									MessageBoard mb,
									MessageBoardConnector pparent,
									BoardAddress selectedRemoteBoardAddress,
									PrintLog outprn){
		
		out = outprn;
	
		importedFactoryProps = factprops; 
		
		this.mbf = mbf;
		this.mb = mb;
		
		parent = pparent ;
		
		remotehostname = selectedRemoteBoardAddress.getAddress().getHostname();
		remoteport = selectedRemoteBoardAddress.getAddress().getPort() ;
		
		(new Thread(this,"MsgBoardSrcInstance")).start() ;
	}	
		
	public void run() {
		 create();
	}

	private void create(){
		
		
		try{
			
			
			
			// we create our own instance....
			// these factoryProperties are local for this embedded client !!  
			factoryProperties = new MessageBoardFactoryProperties()  ;

			// ... but take important settings from the parent = the MessageBoard !

			// defining that we are a client = participant, not the board
			factoryProperties.setTypeOfInstance( MessageBoardFactoryProperties.variant_BSOURCE );
			
			
			// if this is empty, the user home directory will be taken <user-home>/mb/...
			factoryProperties.setHomePath( "D:/dev/java/data/test/mb/" ) ;
			factoryProperties.setProtocolId( MessageBoardFactoryProperties.protocolUDP ) ;
			factoryProperties.setProtocolId( MessageBoardFactoryProperties.protocolTCP ) ;
			
			
			factoryProperties.setRemoteInstancesFlag( false ) ; // false == only local addresses
			factoryProperties.setProtocolId(MessageBoardFactoryProperties.protocolHREST);
			
			// VM dev box
			
		 	factoryProperties.setHostAddressName( remotehostname, remoteport ); // e.g. ("192.168.0.13", 7070) ;
			// TODO create a completely different channel through virtual port 7007
				
			factory = new ParticipantFactory( factoryProperties , this ) ;

			// factory.config().setActiveReceiptsExchange( true ) ;
			factory.config().setName( mb.getSimpleName() + "-embeddedSrc") ;
			factory.config().setDefaultDocType("*") ;

			factory.config().setNumberOfConnectRetry(3) ; 
			factory.config().setDelayBetweenConnectRetries(4950);
			factory.config().setDelayProgressionFactor(1.4) ; 
			factory.config().setInstanceAsUnique( true ) ; 
			 
			// we may set a preferred id of this instance, conflicts are resolved by providing the next available id
			factory.config().setPreferredID(1007);
			factory.config().setPreferredUID( mb.getMxReg().getSuffixUID() );
			 
			// xreg.init(mbf, (ReceptorDigestIntf)msgDigester) ;
			
			// using the wrong virtual port 
			participant = (ParticipantSourceIntf)factory.connect() ;
			
			if (participant!=null) {
				if (participant.getStatus() == ParticipantFactory.PS_CONNECTED ){
					connected = true;
				}
				parent.msgBoardAsParticipant = participant ;
				
        		Vector<XRegistration>  regi = factory.getMbf().getRegisteredInstances() ;
        		
        		if (regi!=null){
        			XRegistration xreg = regi.get(0);
        			if (xreg!=null){
        				name = xreg.getNameOfApplicant();
        			}
        		}
        		factory.setMessageBoardReference(mb);
			}

			
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}
	  

	// =============== local methods ===============
 
	// =============== interfaced methods ===============
	
	// finally, here we will find the results 
	  
	@Override
	public void resultsProvider(Vector<TaskPackage> resultpackages) {
		// TODO Auto-generated method stub
		out.printErr(1, "\r\nResults for abc\n");
	}
	
	
	@Override
	public void currentStateMessage(String str) {
		
		out.printErr(4, "MsgSourceInstance :: SrcInstance :: currentStateMessage()");
		out.print(2,"Simple State Message : "+str);
		
	}

	
	 
	// =============== wrapped methods ===============
 	
	public void xsend( String xmlout, Observer parentObs ){
		 
									out.print(3, "embedded client is going to send : \n");
									out.print(4, ""+xmlout);
		// the important part of the message is:
		// <request doctype="doc" type="relay" secret="87de6ddb-3015-49e7-b407-7651afc52eda" /> 
		participant.send(xmlout,mb) ;
	}
    
	public void disconnect(){
		if (participant!=null){
			participant.disconnect() ;
		}
	}
 

	public ParticipantSourceIntf getParticipant() {
		return participant;
	}


	public boolean isConnected() {
		return connected;
	}

	@Override
	public void resultsProvider(TaskPackage resultpackage) {
		// TODO Auto-generated method stub
		
	}


	 
	
}